<?php

namespace App\Library\OBA;

/**
 * OBA 对接接口
 */
class ObaFunction
{
    protected  $appcode = '0ak1vlw6jqy4m8fchkylfou4i3bf4jphl1uyx7cpg79kmuyk2p3kmlz1eq0x2vzi';

    protected $key = 'qbqy40dvwnkyhu7p6l2pna8m10nynmmi';

    protected $address;

    protected $privateKey;

    protected $domain = 'https://block1.51tuanli.com';

    /**
     * 创建一个新钱包地址
     * @return bool [type] [description]
     */
    public function createWalletAddress()
    {
        //OBA接口地址
        $url = $this->domain . '/api/v1/Bap/get_wallet_addr';
        //时间戳
        $timestamp = time();
        //随机字符
        $noncer = $this->getNoncer();
        //获取签名
        $sign = $this->getSign($timestamp, $noncer);
        //请求参数
        $param = [
            'appcode' => $this->appcode,
            'timestamp' => $timestamp,
            'noncer' => $noncer,
            'sign' => $sign,
            'salt' => 'hehe'
        ];
        //参数转化
        $param = http_build_query($param);
        //模拟请求
        $res = $this->request_post($url);
        //json转换
        $res = json_decode($res);
        //执行成功
        if ($res->error_code == 0) {
            $data = $res->data;

            return $data;
        }

        return false;
    }

    /**
     * 获取地址余额
     * @param  [type] $address [description]
     * @return bool [type]          [description]
     */
    public function getBalanceByAddress($address)
    {
        //地址
        $url = $this->domain . '/api/v1/block/wallet_balance';
        //时间戳
        $timestamp = time();
        //随机字符
        $noncer = $this->getNoncer();
        //获取签名
        $sign = $this->getSign($timestamp, $noncer);
        //请求参数
        $param = [
            'appcode' => $this->appcode,
            'timestamp' => $timestamp,
            'noncer' => $noncer,
            'sign' => $sign,
            'salt' => 'hehe',
            'wallet_addr' => $address
        ];
        //参数转化
        $param = http_build_query($param);
        //post
        $res = $this->request_post($url, $param);
        //json转化
        $res = json_decode($res);
        //dd($res);
        //执行成功
        if ($res->error_code == 0) {
            //返回数据
            $data = $res->data;
            //第一条
            $balance = $data[0]->balance;
            //如果有余额
            if ($balance > 0) {
                //余额换算
                $balance = $this->fromWei($balance);
            }

            return $balance;
        }
        //返回错误
        return ['status' => 0, 'msg' => config('oba_error_code')[$res->error_code]];
    }

    /**
     * 验证地址是否合法
     * @param  [type] $address [description]
     * @return bool [type]          [description]
     */
    public function addressValidator($address)
    {
        //oba接口路径
        $url = $this->domain . '/api/v1/Bap/verify_wallet_addr';
        //时间戳
        $timestamp = time();
        //随机字符
        $noncer = $this->getNoncer();
        //获取签名
        $sign = $this->getSign($timestamp, $noncer);
        //请求参数
        $param = [
            'appcode' => $this->appcode,
            'timestamp' => $timestamp,
            'noncer' => $noncer,
            'sign' => $sign,
            'salt' => 'hehe',
            'wallet_addr' => $address
        ];
        //参数转化
        $param = http_build_query($param);
        //模拟请求
        $res = $this->request_post($url, $param);
        //json转换
        $res = json_decode($res);
        //验证成功
        if ($res->error_code == 0) {
            //返回成功请求
            return ['status' => 1, 'msg' => '地址正确'];
        }
        //验证失败
        return false;
    }

    /**
     * 发起交易
     */
    public function makeTransaction($amounts, $fromAddress, $toAddress, $privateKey)
    {
        //加密私钥
        $key = $this->encryptPrivateKey($fromAddress, $privateKey);
        //单位转换
        $income_sum = $this->toWei($amounts);
        //dump($income_sum);
        $fee = 10000; //最低手续费
        //OBA接口地址
        $url = $this->domain . '/api/v1/block/make_trans_hex?from_wallet';
        //时间戳
        $timestamp = time();
        //随机字符
        $noncer = $this->getNoncer();
        //获取签名
        $sign = $this->getSign($timestamp, $noncer);
        //请求参数
        $param = [
            'appcode' => $this->appcode,
            'timestamp' => $timestamp,
            'noncer' => $noncer,
            'sign' => $sign,
            'salt' => 'hehe',
            'from_wallet' => $fromAddress,
            'k' => $key,
            'to_wallet' => $toAddress,
            'income_sum' => $income_sum,
            'fee' => $fee,
            'time' => time(),
        ];
        //参数转化
        $param = http_build_query($param);
        //模拟请求
        $res = $this->request_post($url, $param);
        //转换json
        $res = json_decode($res);
        //执行成功
        if ($res->error_code == 0) {
            //hex值
            $hex = $res->data->trans_hex;
            //OBA验证交易信息接口
            $h_url = $this->domain . '/api/v1/block/verify_trans';
            $h_param = [
                'appcode' => $this->appcode,
                'timestamp' => $timestamp,
                'noncer' => $noncer,
                'sign' => $sign,
                'salt' => 'hehe',
                'hex' => $hex
            ];
            //模拟请求
            $h_res = $this->request_post($h_url, $h_param);
            //json转换
            $h_res = json_decode($h_res);
            //dd($h_res);
            if ($h_res->error_code == 0) {
                return ['status' => 1, 'msg' => '发起交易成功，请等待区块确认'];
            }

            return ['status' => 0, 'msg' => config('oba_error_code')[$h_res->error_code]];
        }

        return ['status' => 0, 'msg' => config('oba_error_code')[$res->error_code]];
    }

    /**
     * 转为微单位
     */
    private function toWei($value)
    {
        return $value * 100000000;
    }

    /**
     * 微单位转换
     */
    private function fromWei($value)
    {
        return $value / 100000000;
    }

    /**
     * 加密私钥
     */
    private function encryptPrivateKey($from_wallet, $key)
    {
        $time = time();
        $secret_key = substr( $this->H160($from_wallet . $time), 0, 32);

        $iv = $this->binOut(substr($time,-4),16);

        $encrypted = openssl_encrypt($key, 'aes-256-cbc', $secret_key, OPENSSL_RAW_DATA, $iv);

        return base64_encode($encrypted);
    }

    /**
     * hash160签名
     * @param  str $str  被运算的字符串
     * @return str 两次hash160输出
     */
    private function H160($str)
    {
        return hash('RIPEMD160', $str);
    }

    /**
     * 计算10进制的2进制输出，并高位补0，溢出截取
     * @param  str $dec  被运算的十进制字符串
     * @param  int $max  最大长度
     * @return str 两次hash160输出
     */
    private function binOut($dec, $max) {
        $val = gmp_init($dec,10);
        $out =  str_pad( gmp_strval($val,2), $max, "0" , STR_PAD_LEFT);

        return $out;
    }

    /**
     * 签名
     */
    private function getSign($timestamp, $noncer)
    {
        //消息
        $message = $timestamp . $noncer;
        //计算签名
        $sign = bin2hex (mhash(MHASH_SHA256, $message, $this->key));

        return $sign;
      }

    /**
     * 随机字符串
     */
    private function getNoncer()
    {
        //字符源
        $string = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
        //打乱字符
        str_shuffle($string);
        //返回
        return substr($string, 0, 10);
    }

    /**
     * 模拟post请求
     */
    protected function request_post($url = '', $param = '')
    {
        if (empty($url)) {
            return false;
        }
        $postUrl = $url;
        $curlPost = $param;
        $ch = curl_init();//初始化curl
        curl_setopt($ch, CURLOPT_URL,$postUrl);//抓取指定网页
        curl_setopt($ch, CURLOPT_HEADER, 0);//设置header
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);//要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_POST, 1);//post提交方式
        curl_setopt($ch, CURLOPT_POSTFIELDS, $curlPost);
        $data = curl_exec($ch);//运行curl
        curl_close($ch);

        return $data;
    }

    protected function request_get($url)
    {
        // 初始化一个 cURL 对象 
        $curl = curl_init();
        // 设置你需要抓取的URL 
        curl_setopt($curl, CURLOPT_URL, $url); 
        // 设置header 响应头是否输出
        curl_setopt($curl, CURLOPT_HEADER, 1); 
        // 设置cURL 参数，要求结果保存到字符串中还是输出到屏幕上。
        // 1如果成功只将结果返回，不自动输出任何内容。如果失败返回FALSE 
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 0);
        // 运行cURL，请求网页 
        $data = curl_exec($curl); 
        // 关闭URL请求 
        curl_close($curl);

        return $data;
    }
}
